#pragma once
#include <iostream>
#include <vector>
#include <string>
#include <queue>
#include <functional>
#include "Thread.hpp"
#include "Log.hpp"

const size_t g_TNum = 5;
template<class T>
class ThreadPool
{
private:
    bool IfEmpty()
    {
        return _TaskQueue.empty();
    }
    void HandlerTask(std::string name)
    {
        while(true)
        {
            pthread_mutex_lock(&_mutex);  
            while(IfEmpty() && _ifRuning)
            {
                _sleep_thread_num++;//该线程进入到条件变量下等待，记录进入休眠线程的计数器_sleep_thread_num++
                pthread_cond_wait(&_cond, &_mutex); 
                _sleep_thread_num--;//该线程醒来记录进入休眠线程的计数器_sleep_thread_num--
            }
            if(IfEmpty() && !_ifRuning)
            {
                std::cout << name  << " quit" << std::endl;
                pthread_mutex_unlock(&_mutex);
                break;
            }
            T task = _TaskQueue.front();
            _TaskQueue.pop();
            pthread_mutex_unlock(&_mutex); 
            task();//线程执行任务是并发执行的，所以要在临界区外
        }
    }
    
    //将线程池处理成单例模式就需要将拷贝构造和拷贝赋值禁掉
    ThreadPool(const ThreadPool<T> &) = delete;
    void operator=(const ThreadPool<T> &) = delete;

public:
    ThreadPool(size_t TNum = g_TNum)
    :_TNum(g_TNum)
    {
        pthread_mutex_init(&_mutex, nullptr);
        pthread_cond_init(&_cond,nullptr);  
    }
    //初始化应该做：每个线程起一个线程名，所要执行的任务：1.从阻塞队列中读任务 2.执行任务(类中的成员函数)
    void Init()
    {
        func_t func = std::bind(&ThreadPool::HandlerTask, this, std::placeholders::_1);
        //bind绑定this指针参数，对类中的非静态成员函数包装，返回一个可调用对象，该可调用对象就可以少传一个参数：this指针
        for(int i=0; i<_TNum; ++i)
        {
            std::string threadName = "thread-" + std::to_string(i+1);
            _TPool.emplace_back(threadName, func);//用emplace_back()成员方法插入，直接构造
        }
    }
    //让线程池运行任务
    void Start()
    {
        _ifRuning = true;//启动线程池时将表示线程池状态的_ifRuning设为true，表示线程池是运行状态
        for(auto& thread:_TPool)
        {
            thread.Start();
        }
    }
    //暂停线程池，前提是将线程池中还没有任务完的任务执行完
    void Stop()
    {
        //暂停线程先将_ifRuning置为false，然后唤醒在条件变量下等待的所有线程
        _ifRuning = false; 
        pthread_cond_broadcast(&_cond); //唤醒所有线程
    }
    //单例模式
    /*static ThreadPool<T> *GetInstance()
    {
        //判断是否是第一次创建
        if (_tp == nullptr)
        {
            if (_tp == nullptr)
            {
                _tp = new ThreadPool();
                _tp->Init();
                _tp->Start();
            }
        }
        return _tp;
    }*/
    //上面的代码在单线程中是没有问题的，如果在多线程下就问题了，所以需要加锁，这把锁也必须是静态的
    static ThreadPool<T>* GetInstance()
    {
        if (_tp == nullptr)
        {
            pthread_mutex_lock(&_sig_mutex); 
            if (_tp == nullptr)
            {
                _tp = new ThreadPool();
                _tp->Init();
                _tp->Start();
            }
            pthread_mutex_unlock(&_sig_mutex);
        }
        return _tp;
    }
    //向线程池写任务
    void Equeue(const T& in)
    {
        pthread_mutex_lock(&_mutex); 
        if(_ifRuning)//线程池被暂停就不能向线程池写任务
        {
            _TaskQueue.push(in);  
            if(_sleep_thread_num > 0)
            {
                pthread_cond_signal(&_cond);
            } 
        }
        pthread_mutex_unlock(&_mutex);
    }
    ~ThreadPool()
    {
        for(auto thread : _TPool)
        {
            thread.join();
        }
        pthread_mutex_destroy(&_mutex);
        pthread_mutex_destroy(&_sig_mutex);
        pthread_cond_destroy(&_cond);
    }
private:
    size_t _TNum;//线程的个数外部自由定
    std::vector<Thread> _TPool;//对线程统一管理
    std::queue<T> _TaskQueue;//存放任务
    bool _ifRuning;
    pthread_mutex_t _mutex;
    pthread_cond_t _cond;
    size_t _sleep_thread_num;
    //静态的成员变量类内声明，类外定义
    static ThreadPool<T>* _tp;

    static pthread_mutex_t _sig_mutex;
};

template <typename T>
ThreadPool<T>* ThreadPool<T>::_tp = nullptr;
template <typename T>
pthread_mutex_t ThreadPool<T>::_sig_mutex = PTHREAD_MUTEX_INITIALIZER;


